package auth_https

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"github.com/gin-gonic/gin"
	"net"
	"net/http"
	"os"
	"path"
	"strings"
	"time"
)

type CertServe struct {
	Router     *gin.Engine `yaml:"-" json:"-"`
	Port       string
	CaCipher   string // CaCipher 暗号(相当于密码的意思)
	CaZipPath  string
	ServerCert string
	serverKey  string
}

// HttpsRunTLS 单端验证
func (c *CertServe) HttpsRunTLS() error {
	//CA证书
	certPool := x509.NewCertPool()
	ca, err := c.autoIssuanceServerCert()
	if err != nil {
		return fmt.Errorf("load ca err: %s", err)
	}

	if ok := certPool.AppendCertsFromPEM(ca); !ok {
		return fmt.Errorf("证书追加失败")
	}

	//单端验证使用
	return c.Router.RunTLS(c.Port, c.ServerCert, c.serverKey)
}

// HttpsRunTLSDoubleCert 双端验证
func (c *CertServe) HttpsRunTLSDoubleCert() error {
	//CA证书
	certPool := x509.NewCertPool()
	ca, err := c.autoIssuanceServerCert()
	if err != nil {
		return fmt.Errorf("load ca err: %s", err)
	}

	if ok := certPool.AppendCertsFromPEM(ca); !ok {
		return fmt.Errorf("证书追加失败")
	}

	//双端验证使用
	var server = &http.Server{
		Addr:           c.Port,
		Handler:        c.Router,
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
		TLSConfig: &tls.Config{
			ClientAuth: tls.RequireAndVerifyClientCert,
			//服务端设置ClientCAs，设置反了都会造成认证不通过。
			ClientCAs: certPool,
		},
	}
	return server.ListenAndServeTLS(c.ServerCert, c.serverKey)
}

// autoIssuanceServerCert 自动化签发服务端证书
func (c *CertServe) autoIssuanceServerCert() (ac []byte, err error) {

	fileNameAll := path.Base(c.ServerCert)
	fileSuffix := path.Ext(c.ServerCert)
	prefix := strings.TrimSuffix(fileNameAll, fileSuffix)

	if prefix == "." && fileSuffix == "" {
		c.ServerCert, c.serverKey = "./server.crt", "./server.key"
	} else if fileSuffix == "" {

		// 使用 MkdirAll 创建目录
		err := os.MkdirAll(c.ServerCert, 600)
		if err != nil {
			return nil, fmt.Errorf("创建目录失败:%s", err.Error())
		}

		c.serverKey = c.ServerCert
		// 判断路径最后一个字符是否为 "/"
		if strings.HasSuffix(c.ServerCert, "/") {
			fmt.Println("路径以 '/' 结尾")
			c.ServerCert += "server.crt"
			c.serverKey += "server.key"
		} else {
			fmt.Println("路径不以 '/' 结尾")
			c.ServerCert += "/server.crt"
			c.serverKey += "/server.key"
		}
	} else {
		return nil, fmt.Errorf("证书存放目录不能有后缀名: %s\n", c.ServerCert)
	}

	ac, ak, err := OpenZip(c.CaZipPath, c.CaCipher)
	if err != nil {
		return nil, fmt.Errorf("打开zip 失败:%s", err.Error())
	}

	ipv4, _, err := GetLocalIPs()
	if err != nil {
		return nil, fmt.Errorf("获取 ipv4 地址失败:%s", err.Error())
	}

	sc, sk, err := serverCertificate(ac, ak, ipv4)
	if err != nil {
		return nil, fmt.Errorf("证书签发失败:%s", err.Error())
	}

	//保存 server 证书
	err = createCrtToKey(c.ServerCert, c.serverKey, sc, sk)
	if err != nil {
		return nil, fmt.Errorf("保存 server 证书 err:%s", err.Error())
	}

	return ac, nil
}

func GetLocalIPs() ([]net.IP, []net.IP, error) {
	var ipv4List, ipv6List []net.IP
	// 获取所有网络接口
	interfaces, err := net.Interfaces()
	if err != nil {
		return nil, nil, err
	}

	for _, iface := range interfaces {
		// 获取接口的所有地址
		addrs, err := iface.Addrs()
		if err != nil {
			continue
		}

		for _, addr := range addrs {
			var ip net.IP
			switch v := addr.(type) {
			case *net.IPNet:
				ip = v.IP
			case *net.IPAddr:
				ip = v.IP
			}

			// 过滤掉不需要的地址
			if ip == nil || ip.IsLoopback() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() {
				continue
			}
			if ip.To4() != nil {
				////过滤掉169.254.*.*和172.*.*.*地址
				//ipStr := ip.String()
				//if strings.HasPrefix(ipStr, "169.254.") || strings.HasPrefix(ipStr, "172.") {
				//	continue
				//}
				ipv4List = append(ipv4List, ip)
			} else if ip.To16() != nil {
				ipv6List = append(ipv6List, ip)
			}
		}
	}
	return ipv4List, ipv6List, nil
}
